/** File name: cellNetwork.cu **/

#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <cuda.h>
#include "cellNetwork.h"
#include <Xlib.h>
#include "data.cu"
#include "network.cu"
#include "view.h"

#define WINDOW_TITLE "Simulating crickets by using Cellular Automata and CUDA"
#define NUM_CYCLE 20

const int nbCells = sizeof(Cells)/sizeof(CellArray);
//If the number of cells between network and data are different, chosing the smaller one 
int dataSize = ((nbCells > NODE_NUMBER) ? NODE_NUMBER : nbCells);

int main()
{
	int plotSize, NSize, blockSize, nodeSize, channelSize, nodeStateSize;
	int *now_h, *next_h, *now_d, *next_d;
	canaux *channels_d;
	Colormap colormap;
	NodeState *nowState_h, *nowState_d, *nextState_h, *nextState_d, *buffState_d;
	dim3 tpb(NODE_NUMBER,1,1);
        curandState* devStates_d;

	plotSize = 1;
	NSize = 256;
	blockSize = 512;
	nodeSize = NODE_NUMBER * sizeof(int);
	channelSize = NODE_NUMBER * sizeof(canaux);	
	nodeStateSize = NODE_NUMBER * sizeof(NodeState);

	Display *dsp = XOpenDisplay(NULL);	
	if (!dsp){ return 1;}
	colormap = DefaultColormap(dsp, 0);

	int screenNumber = DefaultScreen(dsp);
	unsigned long white = WhitePixel(dsp, screenNumber);
	unsigned long black = BlackPixel(dsp, screenNumber);	

	Window win = XCreateSimpleWindow(dsp,
				DefaultRootWindow(dsp),
				50, 50,// origin
				NSize*plotSize*3, NSize*plotSize*3,// size
				0, black,//border
				white//background
	);

	XStoreName(dsp, win, WINDOW_TITLE);
	XMapWindow(dsp, win);
	
	GC gc = XCreateGC(dsp, win, 0, NULL);	
	
	long eventMask = StructureNotifyMask;
	XSelectInput(dsp, win, eventMask);
	XEvent evt;
	do
	{
		XNextEvent(dsp, &evt);
	}while(evt.type != MapNotify);	
		
	/***********/
	//Allocating memory to host variables
	now_h = (int*)malloc(nodeSize);
	next_h = (int*)malloc(nodeSize);
	nowState_h = (NodeState*) malloc(nodeStateSize);
	nextState_h = (NodeState*) malloc(nodeStateSize);
	
	//Allocating memory to device variable
	cudaMalloc((void**)&now_d, nodeSize);
	cudaMalloc((void**)&next_d, nodeSize);
	cudaMalloc((canaux**)&channels_d, channelSize);
	cudaMalloc((NodeState**)&nowState_d, nodeStateSize);
	cudaMalloc((NodeState**)&nextState_d, nodeStateSize);
	cudaMalloc((NodeState**)&buffState_d, nodeStateSize);
        cudaMalloc((curandState**)&devStates_d, NODE_NUMBER * sizeof(curandState));

	// setup seeds for random number generator
    	setup_kernel <<< 1, tpb >>> ( devStates_d, time(NULL) );

	///// New version: Pollution diffusion
	initStateCricket(nowState_h, NODE_NUMBER);
	//initStateCricket<<<(NODE_NUMBER / blockSize) + 1, blockSize>>>(nowState_d, devStates_d, NODE_NUMBER);
	cudaMemcpy(nowState_d, nowState_h, nodeStateSize, cudaMemcpyHostToDevice);
	cudaMemcpy(channels_d, channels_h, channelSize, cudaMemcpyHostToDevice);

	printf("\nInitilizating image ...\n");

	int nbCrickets;
	for (int j = 0; j < NODE_NUMBER; j++)
	{
		nbCrickets = nowState_h[j].nbCrickets;
		//if (j < 30) printf("%d ", nbCrickets);
	    	showCellInsect(Cells[j], dsp, colormap, win, gc, plotSize, nbCrickets);
	}

	printf("\nClick on the window to continue!\n");

	eventMask = ButtonPressMask | ButtonReleaseMask;

	XSelectInput(dsp, win, eventMask);
	do{
		XNextEvent(dsp, &evt);
	}while(evt.type != ButtonRelease);
	
	NodeState *temp;

	printf("Simulation is running: %d cycles...\n", NUM_CYCLE);

	for (int i = 0; i < NUM_CYCLE; i++)
	{
	    stepStateCricket<<<(NODE_NUMBER / blockSize) + 1, blockSize>>>(nowState_d, nextState_d, channels_d, NODE_NUMBER, devStates_d);

	    updateNodeState<<<(NODE_NUMBER / blockSize) + 1, blockSize>>>(nowState_d, nextState_d, NODE_NUMBER);

	    cudaMemcpy(nextState_h, nextState_d, nodeStateSize, cudaMemcpyDeviceToHost);

	    printf("--Cycle %d/%d finished!\n  Refreshing viewer ...\n", i + 1, NUM_CYCLE);
	
	    for (int j = 0; j < NODE_NUMBER; j++)
	    {
		nbCrickets = nextState_h[j].nbCrickets;
		//if (j < 30) printf("%d ", nbCrickets);
	    	showCellInsect(Cells[j], dsp, colormap, win, gc, plotSize, nbCrickets);
	    }
	    //printf("\n");
	    //printf("Refreshing finished!\n");
	    //Preparing for the next step	
	    temp = nextState_d;
	    nextState_d = nowState_d;
	    nowState_d = temp;	    	    	    	    
	}
	
	printf("\nClick on window to exit!\n");

	eventMask = ButtonPressMask | ButtonReleaseMask;

	XSelectInput(dsp, win, eventMask);
	do{
		XNextEvent(dsp, &evt);
	}while(evt.type != ButtonRelease);

	//Releasing resources
	XDestroyWindow(dsp, win);
	XCloseDisplay(dsp);
		
	cudaFree(now_d);
	cudaFree(next_d);
	cudaFree(channels_d);
	cudaFree(nowState_d);
	cudaFree(nextState_d);
	cudaFree(buffState_d);
	cudaFree(devStates_d);

	free(now_h);
	free(next_h);
	free(nowState_h);
	free(nextState_h);
	return 0;
}
